package main

import (
	"fmt"
	"strconv"
)

/*
1. 关键字 defer 用于注册延迟调用(本方法内defer后面的语句会在方法return的前一刻才会执行，无论方法是否报错)。
	有点类似于java中 try..catch..finally 中的finally
2. 这些调用直到 return 前才被执。因此，可以用来做资源清理。
3. 多个defer语句，按先进后出的方式执行。
4. defer语句中的变量，在defer声明时就决定了。

defer一般用于最终释放被程序占用的资源，例如网络连接，db连接，文件句柄等
*/
func main() {
	demo1FILOMultiDefer()
	demo2MultiDeferClosure()
	demo2DeferClosure2()
	//demo3DeferPanic()
	demo3DeferPanic2()
}

// 同一个方法多个defer，按照先入后出顺序执行
/*
hello world demo1
defer--6
defer--5
defer--4
defer--2
defer--1

*/
func demo1FILOMultiDefer() {
	defer fmt.Println("defer--1")
	defer fmt.Println("defer--2")
	fmt.Println("hello world demo1")
	for i := 4; i < 7; i++ {
		defer fmt.Println("defer--" + strconv.Itoa(i))
	}
}

// defer + 闭包
/*
hello demo2
defer--2
defer--1
defer--0
4
4
4
4
*/
func demo2MultiDeferClosure() {
	for i := 0; i < 4; i++ {
		defer func() {
			fmt.Println(i)
		}() // 由于闭包的性质，闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.
	}
	fmt.Println("hello demo2")

	// 如果需要在循环里面写defer，就需要使用一个额外的变量，来防止最终闭包变量变化带来的影响
	for i := 0; i < 3; i++ {
		t := i // 防止闭包内部引用外部变量而逻辑错乱
		defer func() {
			fmt.Println("defer--" + strconv.Itoa(t))
		}()
	}
}

/*
x = 20 y = 120
defer: 10 120
*/
func demo2DeferClosure2() {
	x, y := 10, 20

	defer func(i int) {
		println("defer:", i, y) // y 闭包引用
	}(x) // x 被复制

	x += 10
	y += 100
	println("x =", x, "y =", y)
}

// 即使defer后面的函数语句报错，defer也会在最后执行
/*
demo3 defer---2
demo3 defer---1
panic: runtime error: integer divide by zero

*/
func demo3DeferPanic() {
	defer fmt.Println("demo3 defer---1")
	defer fmt.Println("demo3 defer---2")
	a := 0
	fmt.Println(100 / a)                          // 模拟报错
	defer fmt.Println("demo3 defer cannot reach") // 在报错语句之后的defer无法执行

}

/*
defer---22
defer---11
panic: sdclone panic...
*/
func demo3DeferPanic2() {
	defer fmt.Println("defer---11")
	defer fmt.Println("defer---22")
	panic("sdclone panic...") // 类似于java的throw new XXXException(...)
}
